﻿namespace SimpleX.RBAC
{
    public class AuthService : IAuthService
    {
        private readonly ISysUserService _sysUserService;
        private readonly ICacheService _cacheService;
        private readonly ICapPublisher _capPublisher;

        public AuthService(ISysUserService sysUserService,
            ICacheService cacheService,
            ICapPublisher capPublisher)
        {
            _sysUserService = sysUserService;
            _cacheService = cacheService;
            _capPublisher = capPublisher;
        }

        public async Task<LoginUserOutput> GetLoginUser()
        {
            var user = await _sysUserService.GetSysUserById(UserManager.UserId);//根据账号获取用户信息
            if (user != null)
            {
                var output = user.Adapt<LoginUserOutput>();
                output.ButtonCodeList = await _sysUserService.OwnButtonCodeList();
                return output;
            }
            return null;
        }

        public bool CheckTokenInRedis(string token)
        {
            if (string.IsNullOrWhiteSpace(UserManager.UserId) || string.IsNullOrWhiteSpace(UserManager.Device))
            {
                return false;
            }
            var key = CacheConst.Cache_UserToken + UserManager.UserId + ":" + UserManager.Device;
            var redisToken = _cacheService.Get<string>(key);
            return token == redisToken;
        }

        public async Task<LoginOutput> Login(LoginInput loginInput)
        {
            var user = await _sysUserService.GetSysUserByAccount(loginInput.Account);

            //直接返回E001对应的错误信息

            //return Unify.SetError(App.L("E001"));
            //return Unify.SetErrorL("E001");

            //错误信息使用 string.format
            if (user == null)
                return Unify.SetErrorL("E001", loginInput.Account);

            if (user.UserStatus != "Y")
                return Unify.SetError("用户名已经被禁用");

            if (user.Password == loginInput.Password)
            {
                //返回结果 上次登录为空,或者距离上次登录已经30天了,则需要修改密码
                if (user.LastLoginTime == null || Math.Abs((DateTime.Now - user.LastLoginTime.Value).TotalDays) >= 30)
                {
                    return new LoginOutput
                    {
                        Token = "PLEASE CHANGE PASSWORD FIRST",
                        Account = user.Account,
                        Name = user.Name,
                        MustChangePwd = true
                    };
                }

                //引入设备概念,每次登陆都重新颁发Token
                var token = JwtHelper.GenerateToken(new Dictionary<string, string>
                {
                    [ClaimConst.UserId] = user.Id,
                    [ClaimConst.Account] = user.Account,
                    [ClaimConst.Name] = user.Name,
                    [ClaimConst.SuperAdmin] = user.SuperAdmin,
                    [ClaimConst.Device] = loginInput.Device
                });

                var key = CacheConst.Cache_UserToken + user.Id + ":" + loginInput.Device;
                _cacheService.Set(key, token, JwtHelper._jwtSettings.ExpirationMinutes * 60);

                _capPublisher.Publish("logined", user.Id);

                App.HttpContext.Response.Headers["access-token"] = token;
                //App.HttpContext.Response.Headers["x-access-token"] = token;

                return new LoginOutput
                {
                    Token = token,
                    Account = user.Account,
                    Name = user.Name,
                };
            }

            return Unify.SetError("用户名密码错误");
        }

        public Task LoginOut(LoginOutInput input)
        {
            var key = CacheConst.Cache_UserToken + UserManager.UserId + ":" + input.Device;
            _cacheService.Remove(key);

            return Task.CompletedTask;
        }

        public PicValidCodeOutPut GetCaptchaInfo()
        {
            //生成验证码
            var captchInfo = CaptchaUtil.CreateCaptcha(CaptchaType.ARITH, 4, 100, 38);
            //生成请求号，并将验证码放入redis
            var reqNo = IDUtils.GetId();
            _cacheService.Set(CacheConst.Cache_Captcha + reqNo, captchInfo.Code, TimeSpan.FromMinutes(5));
            //返回验证码和请求号
            return new PicValidCodeOutPut { ValidCodeBase64 = captchInfo.Base64Str, ValidCodeReqNo = reqNo };
        }

        [CapSubscribe("logined")]
        public async Task LoginedAsync(string userId)
        {
            await _sysUserService.SetLogined(userId);
        }
    }
}